/*
 * Copyright 2016-2019 yoara
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package yhao.infra.feature.solr.local;

import org.apache.commons.lang.StringUtils;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import yhao.infra.common.ContextHolder;
import yhao.infra.common.model.Pagination;
import yhao.infra.feature.solr.local.core.SolrServerFactory;
import yhao.infra.feature.solr.local.core.SolrTemplate;
import yhao.infra.feature.solr.local.core.facet.FacetSolrPagination;
import yhao.infra.feature.solr.local.core.group.GroupSolrPagination;
import yhao.infra.feature.solr.local.core.stats.StatsSolrPagination;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

/**
 * 所有查询器的基类
 *
 * @see SolrTemplate
 * @see yhao.infra.feature.solr.local.core.SolrCallbackHandler
 * @author yoara
 */
public abstract class AbstractSearcher<T> implements Searcher<T> {
    protected SolrServerFactory solrServerFactory;
    protected String solrServerUrl;                 //非solrCloud则设置该字段
    protected String zkHost;                        //solrCloud地址
    protected String zkRoot;                        //zk 路径
    protected String solrName;                      //solr实例名
    protected Integer zkClientTimeout = 10000;      //请求超时时间
    protected Integer zkConnectTimeout = 10000;     //链接超时时间

    protected SolrTemplate solrTemplate;

    public AbstractSearcher(SolrServerFactory solrServerFactory) {
        this.solrServerFactory = solrServerFactory;
    }

    /**初始化基于zk的solrCloud**/
    public void initSolrCloud(String solrName,String zkHost){
        initSolrCloud(solrName,zkHost,this.zkRoot,this.zkClientTimeout,this.zkConnectTimeout);
    }

    /**初始化基于zk的solrCloud**/
    public void initSolrCloud(String solrName,String zkHost,String zkRoot){
        initSolrCloud(solrName,zkHost,zkRoot,this.zkClientTimeout,this.zkConnectTimeout);
    }

    /**初始化基于zk的solrCloud**/
    public void initSolrCloud(String solrName,String zkHost,String zkRoot,
                              Integer zkClientTimeout,Integer zkConnectTimeout){
        if(StringUtils.isNotEmpty(solrServerUrl)){
            throw new RuntimeException("已经设置solrServerUrl，请勿重复设置zkHost");
        }
        this.solrName = solrName;
        this.zkHost = zkHost;
        this.zkRoot = zkRoot;
        this.zkClientTimeout = zkClientTimeout;
        this.zkConnectTimeout = zkConnectTimeout;
        initSolrTemplate();
    }

    /**初始化独立节点的solr**/
    public void initSolrServer(String solrName,String solrServerUrl){
        if(StringUtils.isNotEmpty(zkHost)){
            throw new RuntimeException("已经设置zkHost，请勿重复设置solrServerUrl");
        }
        this.solrName = solrName;
        this.solrServerUrl = solrServerUrl;
        initSolrTemplate();
    }

    private void initSolrTemplate(){
        if(solrTemplate==null){
            solrTemplate = new SolrTemplate(this);
        }
    }

    public SolrClient getSolrServer(){
        SolrClient solrServer = null;
        if(zkHost!=null){//当使用zkHost的时候 就视为使用了solrCloud
            solrServer = solrServerFactory.getCloudSolrServer(
                    zkHost,zkRoot, solrName, zkClientTimeout, zkConnectTimeout);
        }else{
            if(solrServerUrl == null) {
                throw new RuntimeException("solrServerUrl must be specified");
            }
            solrServer = solrServerFactory.getCommonsHttpSolrServer(solrServerUrl);
        }
        return solrServer;
    }

    protected String getDataSource(){
        return ContextHolder.getDataSource().toLowerCase();
    }

    public Pagination<T> query(SearchCriteria criteria, Pagination<T> pagination) {
        if (criteria.getGroupQueryInfo()!=null){
            if(!(pagination instanceof GroupSolrPagination)){
                throw new RuntimeException("wrong pagination ,group query need GroupSolrPagination");
            }
            return solrTemplate.queryGroup(criteria, (GroupSolrPagination)pagination);
        }
        if (criteria.getFacetQueryInfo()!=null) {
            if(!(pagination instanceof FacetSolrPagination)){
                throw new RuntimeException("wrong pagination ,facet query need FacetSolrPagination");
            }
            return solrTemplate.queryFacet(criteria,(FacetSolrPagination)pagination);
        }
        if (criteria.getStatsQueryInfo()!=null) {
            if(!(pagination instanceof StatsSolrPagination)){
                throw new RuntimeException("wrong pagination ,stats query need StatsSolrPagination");
            }
            return solrTemplate.queryStats(criteria,(StatsSolrPagination)pagination);
        }
        Pagination<T> page = solrTemplate.query(criteria, pagination);
        return page;
    }

    public Pagination<T> queryGroup(SearchCriteria criteria, GroupSolrPagination<T> pagination) {
        if(criteria.getGroupQueryInfo()==null){
            throw new RuntimeException("group query need GroupQueryInfo()");
        }
        return solrTemplate.queryGroup(criteria, pagination);
    }

    public Pagination<T> queryFacet(SearchCriteria criteria, FacetSolrPagination<T> pagination) {
        if(criteria.getFacetQueryInfo()==null){
            throw new RuntimeException("group query need FacetQueryInfo");
        }
        return solrTemplate.queryFacet(criteria, pagination);
    }

    public Pagination<T> queryStats(SearchCriteria criteria, StatsSolrPagination<T> pagination) {
        if(criteria.getStatsQueryInfo()==null){
            throw new RuntimeException("group query need StatsQueryInfo");
        }
        return solrTemplate.queryStats(criteria, pagination);
    }

    public QueryResponse queryResponse(SearchCriteria criteria) throws IOException, SolrServerException {
        return solrTemplate.queryResponse(criteria);
    }

    public QueryResponse queryResponseCustomer(SolrQuery solrQuery) throws IOException, SolrServerException {
        return solrTemplate.queryResponseCustomer(solrQuery);
    }

    @Override
    public void save(T entity) {
        solrTemplate.save(entity);
    }

    @Override
    public void saveList(List<T> entityList) {
        solrTemplate.saveList(entityList);
    }

    @Override
    public void update(SolrUpdateParam update) {
        solrTemplate.update(update);
    }

    //@Override
    //public void updateList(List<T> entityList) {
    //    solrTemplate.updateList(entityList);
    //}

    @Override
    public void deleteById(String id) {
        solrTemplate.deleteById(id);
    }

    @Override
    public void deleteByIds(List<String> ids) {
        solrTemplate.deleteByIds(ids);
    }

    @Override
    public void deleteByQuery(String query) {
        solrTemplate.deleteByQuery(query);
    }

    @Override
    public void optimize(int maxSegments) {
        solrTemplate.optimize(maxSegments);
    }

    @Override
    public void deleteAll() {
        solrTemplate.deleteAll();
    }

    @Override
    public T solrDocumentToEntity(SolrDocument solrDocument) throws Exception {
        if(solrDocument == null){
            return null;
        }

        Type genType = getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        T entity = (T) ((Class) params[0]).getDeclaredConstructor().newInstance();
        java.lang.reflect.Field[] fields = ((Class) params[0]).getDeclaredFields();
        if(fields!=null){
            for(java.lang.reflect.Field field:fields){
                Object value = solrDocument.getFieldValue(field.getName());
                if(value!=null){
                    try {
                        field.setAccessible(true);
                        if(field.getType().isEnum()){
                            //field.set(entity, Enum.valueOf((Enum<T extends ?>)field.getType().getClass(), value));
                            field.set(entity, getEnumObject(field.getType(),value));
                        }else{
                            field.set(entity,value);
                        }
                    } catch (IllegalAccessException e) {}
                }
            }
        }
        return entity;
    }
    private <M> Object getEnumObject(Class<M> type, Object value){
        try {
            M[] enumConstants = type.getEnumConstants();
            for (M ec : enumConstants) {
                if (((Enum<?>) ec).name().equals(value)) {
                    return ec;
                }
            }
        } catch (Exception e) {}
        return null;
    }
}
